package cgl.sensorgrid.sopac.gps.filters;

import java.util.Collections;
import java.util.List;
import java.util.LinkedList;
import cgl.narada.event.NBEvent;
import cgl.sensorgrid.sopac.gps.threadpool.ThreadPool;
import cgl.sensorgrid.filters.Filter;
import java.util.Calendar;
import cgl.sensorgrid.common.ITRFtoLatLon;
import java.nio.ByteOrder;
import cgl.narada.util.ByteUtilities;
import java.text.SimpleDateFormat;
import java.nio.ByteBuffer;
import cgl.narada.service.time.ntptime.TimeServiceImpl;
import cgl.narada.service.time.TimeService;
import cgl.narada.service.*;

class ryoTOasciiFilter {

    private String header = "";
    private String position = "";
    private String xyzVariance = "";
    private String troposphericCovariance = "";
    private String satelliteInfo = "";
    private String RYOmessage = "";

    private String stamp;


    int[] a = new int[1];
    private ryoTOascii publisher;

    public ryoTOasciiFilter(ryoTOascii publisher_) {
        this.publisher = publisher_;
    }

    private Object syncObj1 = new Object();
    private List eventList = Collections.synchronizedList(new LinkedList());

    public void addEvent(NBEvent event) {
    synchronized (syncObj1) {
        eventList.add(event);
//        System.out.println("eventlist size = " + eventList.size());
        if (eventList.size() == 1) {
            syncObj1.notify();
        }
    }
}

  public void run() {
      boolean running = true;
      try {
          while (running) {
              synchronized (syncObj1) {
                  if (eventList.size() == 0) {
                      syncObj1.wait();
                  }

                  NBEvent nbEvent = (NBEvent) eventList.remove(0);

                  if (publisher.getEventPropertyValue(nbEvent, "MSGSTAMP") != null)
                      this.stamp = publisher.getEventPropertyValue(nbEvent, "MSGSTAMP");

                  byte[] data2 = nbEvent.
                                 getContentPayload();
                  if(data2!=null && data2.length>0){
                      processRawMessage(data2);
                      //                      System.out.println(new String(data2));
                  }
              }
          }
          return;
      } catch (Exception ex) {
          ex.printStackTrace();
      }
    }



    /**
     *
     * @param input byte[]
     */
    public void processRawMessage(byte[] input) {
        try {
            boolean validHeader = false;
            if (input.length >= 5) {
                validHeader = decodeHeader(input);
            }
            if (validHeader && input.length > 52) {
                decodePosition(input, 5);
            }

            String pos = this.getPosition();
            if (pos.endsWith("\n")) {
                pos = pos.substring(0, pos.length() - 1);
            }
            if (pos != null && pos.trim() != "" && pos.trim().length() > 0) {

                //Stamp the message before publishing
//                stamp = stamp + "\t" + publisher.getTimeStamp();
                stamp = stamp + "\t" + System.currentTimeMillis();

                publisher.publishDataWithProperty((pos + "\nEOF\n").getBytes(), "MSGSTAMP", stamp);
                //System.out.println(pos);
            }
            this.position = "";

        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    /**
     *
     * @param message String
     * @return String
     */
    public String messageToGML(String message) {
        try {
//            MakeSopacObservationCollection gml = new
//                                                 MakeSopacObservationCollection();
//            return gml.makeSOPACObservationCollection(message);
            return "";
        } catch (Exception ex) {
            ex.printStackTrace();
            return null;
        }
    }


    /**
     *
     * @param input byte[]
     * @return boolean
     */
    public boolean decodeHeader(byte[] input) {
        try {
            boolean validHeader = false;
            cgl.narada.util.ByteUtilities b = new cgl.narada.util.ByteUtilities(
                    a);
            int SyncMarker1 = b.getIntFromByte(input[0]);
            int SyncMarker2 = b.getIntFromByte(input[1]);
            if (Integer.toHexString(SyncMarker1).toUpperCase().equalsIgnoreCase(
                    "9C") &&
                Integer.toHexString(SyncMarker2).toUpperCase().equals("A5")) {
                validHeader = true;
            }
            return validHeader;
        } catch (Exception ex) {
            ex.printStackTrace();
            return false;
        }
    }

    /**
     *
     * @param input byte[]
     * @param offset int
     */
    public String decodePosition(byte[] input, int offset) {
        try {
            String line = "";
            ByteUtilities b = new ByteUtilities(a);
            int i = offset;
            ByteBuffer bb = ByteBuffer.wrap(input);
            bb.order(ByteOrder.LITTLE_ENDIAN);

            //Date
            short GPSWeek = bb.getShort(i);
            int GPSmsOfWeek = bb.getInt(i + 2);
            String date = getDate(Short.toString(GPSWeek),
                                  Integer.toString(GPSmsOfWeek));
            //Site info
            int SiteIndex = b.getIntFromByte(input[i + 6]);
            int SiteCount = b.getIntFromByte(input[i + 7]);
            String SiteId = Character.toString((char) (input[i + 8])) +
                            Character.toString((char) (input[i + 9])) +
                            Character.toString((char) (input[i + 10])) +
                            Character.toString((char) (input[i + 11]));

            //Skip 12 and 13
            //            line += " " + GPSWeek;
            //            line += " " + GPSmsOfWeek;
            line += "\t" + date;
            line += "\t" + SiteId;
            line += "\t" + SiteIndex;
            line += "\t" + SiteCount;

            //ITRF Positions
            double X = bb.getDouble((i + 14));
            double Y = bb.getDouble((i + 22));
            double Z = bb.getDouble((i + 30));
            double T = bb.getDouble((i + 38));
            line += "\t" + X;
            line += "\t" + Y;
            line += "\t" + Z;
            line += "\t" + T;
            //            System.out.println("X " + X);
            //            System.out.println("Y " + Y);
            //            System.out.println("Z " + Z);
            //            System.out.println("T " + T);


            //Lat, Lon, Height positions
            double lat = 0.0;
            double lon = 0.0;
            double hi = 0.0;

            ITRFtoLatLon latlon = new ITRFtoLatLon();
            double[] llh = null;
            llh = latlon.GetLatLonHeightRadians(X, Y, Z);

            if (llh != null && llh.length == 3) {
                line += "\t" + llh[0];
                line += "\t" + llh[1];
                line += "\t" + llh[2];
            }

            String posQualityBytes = b.printByte(input[i + 46]);
            if (!posQualityBytes.equals("") && posQualityBytes.length() == 8) {
                String method = posQualityBytes.substring(3, 6);
                String calc = posQualityBytes.substring(6, 8);

                if (calc.equals("00")) {
                    line += " Undetermined";
                } else if (calc.equals("01")) {
                    line += " L1";
                } else if (calc.equals("10")) {
                    line += " L1/L2";
                }

                if (method.equals("001")) {
                    line += " Undetermined";
                } else if (method.equals("010")) {
                    line += " Absolute";
                } else if (method.equals("011")) {
                    line += " Relative";
                } else if (method.equals("100")) {
                    line += " Phase";
                }
            }

            //int Flag = b.getIntFromByte(input[i + 47]);
            String flagString = b.printByte(input[i + 47]);
            //line += " " + flagString;
            boolean xyz = false;
            boolean tropo = false;
            boolean satellite = false;

            if (!flagString.equals("") && flagString.length() == 8) {
                if (flagString.charAt(7) == '1') {
                    line += " XYZ";
                    xyz = true;
                }
                if (flagString.charAt(6) == '1') {
                    line += " Troposhpheric";
                    tropo = true;
                }
                if (flagString.charAt(5) == '1') {
                    line += " Satellite";
                    satellite = true;
                }
            }

            i += 47;

            if (input.length > 51) {
                if (xyz == true) {
                    i = decodeXYZVariances(input, i + 1);
                }
                line += " " + this.getXyzVariance();
                this.setXyzVariance("");
            }

            if (input.length > i) {
                if (satellite == true) {
                    i = decodeSatellite(input, i + 1);
                }
                line += " " + this.getSatelliteInfo();
                this.setSatelliteInfo("");
            }

            if (line != null && !line.equals("")) {
                this.appendPosition(line.trim() + "\n");
            }

            if (input.length > i + 52) {
                decodePosition(input, i + 7);
            }

            return line;
        } catch (Exception ex) {
            System.err.println("\nException occured in decodePosition()");
            ex.printStackTrace();
            return null;
        }
    }


    /**
     *
     * @param input byte[]
     * @param i int
     * @return int
     */
    public int decodeXYZVariances(byte[] input, int i) {
        try {
            String line = "";
            ByteBuffer bb = ByteBuffer.wrap(input);
            bb.order(ByteOrder.LITTLE_ENDIAN);

            double scale = bb.getDouble(i);
            double Xvar = bb.getDouble((i + 8));
            double Yvar = bb.getDouble((i + 16));
            double Zvar = bb.getDouble((i + 24));
            double YXvar = bb.getDouble((i + 32));
            double YZvar = bb.getDouble((i + 40));
            double ZXvar = bb.getDouble((i + 48));

            i += 55;
            line += " " + scale;
            line += " " + Xvar;
            line += " " + Yvar;
            line += " " + Zvar;
            line += " " + YXvar;
            line += " " + YZvar;
            line += " " + ZXvar;
            this.setXyzVariance(line);
            return i;
        } catch (Exception ex) {
            //            System.err.println("\nException occured in decodeVariances()");
            return i;
        }

    }

    public int decodeTroposphericCoverianceBlock(byte[] input, int i) {
        try {
            String line = "";
            ByteBuffer bb = ByteBuffer.wrap(input);
            bb.order(ByteOrder.LITTLE_ENDIAN);

            double Tvar = bb.getDouble((i + 8));
            double TXcovar = bb.getDouble((i + 16));
            double TYcovar = bb.getDouble((i + 24));
            double TZcovar = bb.getDouble((i + 32));

            i += 33;
            line += " " + Tvar;
            line += " " + TXcovar;
            line += " " + TYcovar;
            line += " " + TZcovar;
            this.setTroposphericCovariance(line);
            return i;
        } catch (Exception ex) {
            //            System.err.println("\nException occured in decodeVariances()");
            return i;
        }

    }

    public int decodeSatellite(byte[] input, int i) {
        try {
            String line = "";
            ByteUtilities b = new ByteUtilities(a);
            ByteBuffer bb = ByteBuffer.wrap(input);
            bb.order(ByteOrder.LITTLE_ENDIAN);

            int satelliteCount = b.getIntFromByte(input[i]);
            // line += " " + satelliteCount;
            double GDOP = bb.getDouble((i + 1));
            // line += " " + GDOP;

            if (satelliteCount > 0) {
                i += 9;
                for (int j = 0; j < satelliteCount; j++) {
                    if (input.length > i + 5) {
                        int PRN = b.getIntFromByte(input[i]);
                        int PRNFlags = b.getIntFromByte(input[i + 1]);
                        short Elevation = bb.getShort(i + 2);
                        short Azimuth = bb.getShort(i + 4);
                        line += " " + (j + 1);
                        line += " " + PRN;
                        line += " " + PRNFlags;
                        line += " " + Elevation;
                        line += " " + Azimuth;
                        i += 6;
                    }
                }
                this.setSatelliteInfo(line);
            }
            return i;
        } catch (Exception ex) {
            //            System.err.println("\nException occured in decodeSatellite()");
            //            ex.printStackTrace();
            return i;
        }
    }


    public void setHeader(String header) {
        this.header = header;
    }

    public void setPosition(String position) {
        this.position = position;
    }

    public void appendPosition(String pos) {
        this.position += pos;
    }


    public void setXyzVariance(String xyzVariance) {
        this.xyzVariance = xyzVariance;
    }

    public void setTroposphericCovariance(String troposphericCovariance) {
        this.troposphericCovariance = troposphericCovariance;
    }

    public void setSatelliteInfo(String satelliteInfo) {
        this.satelliteInfo = satelliteInfo;
    }

    public void setRYOmessage(String header, String position) {
        String RYOmsg = header;
        RYOmsg.trim();

        RYOmsg += position;
        RYOmsg.trim();
        this.RYOmessage = RYOmsg;
    }

    public String getHeader() {
        return header;
    }

    public String getPosition() {
        return position;
    }

    public String getXyzVariance() {
        return xyzVariance;
    }

    public String getTroposphericCovariance() {
        return troposphericCovariance;
    }

    public String getSatelliteInfo() {
        return satelliteInfo;
    }

    public String getRYOmessage() {
        return RYOmessage;
    }

    /**
     * Calculates Gregorian Date from GPS Week and GPS milliseconds
     * @param GPS_week String
     * @param GPS_millisecond String
     * @return String
     */
    public String getDate(String GPS_week, String GPS_millisecond) {
        try {
            //GPS_epoch = "1/6/1980";
            long GPS_start_epoch = 315964800;
            long GPS_leapseconds = 13;

            long time = GPS_start_epoch +
                        Long.parseLong(GPS_week) * 7 * 24 * 3600 +
                        Long.parseLong(GPS_millisecond) / 1000 -
                        GPS_leapseconds;

            Calendar cal = Calendar.getInstance();
            cal.setTimeInMillis(time * 1000);
            SimpleDateFormat myDateFormat =
                    new SimpleDateFormat("yyyy-MM-dd\thh:mm:ssa-z");

            return myDateFormat.format(cal.getTime());
        } catch (Exception ex) {
            ex.printStackTrace();
            return null;
        }
    }

}



/**
 *
 * <p>Title: SensorGrid</p>
 *
 * <p>Description: </p>
 *
 * <p>Copyright: Copyright (c) 2006</p>
 *
 * <p>Company: </p>
 *
 * @author Galip Aydin
 * @version 1.0
 */
public class ryoTOascii extends Filter {

    private static ThreadPool pool = new ThreadPool(10);
    private ryoTOasciiFilter r2a;
    private TimeService t;

    public ryoTOascii(String[] args, String[] filterSpecificArgs) {

            //init the NTP service
            t = TimeServiceImpl.getInstance();

            //Sleep for 5 seconds to init the NTP service
            try {
                System.out.println(
                        "Sleeping for 3 seconds before initializing the NTP service");
                Thread.sleep(3000);
            } catch (InterruptedException ex1) {
                ex1.printStackTrace();
            }


        if (args == null || args.length == 0) {
            System.err.println(
                    "Incorrect number of arguments in ryo2ascii::Constructor...");
            //System.exit(0);
        } else {
            this.initialize(args);
            r2a = new ryoTOasciiFilter(this);
            r2a.run();
        }

    }

    public void onEvent(NBEvent nbEvent) {
        try {

            if (nbEvent.getContentPayload() != null) {

                //-----
                //This block is required for performance tests
                String stamp = "";

                if (this.getEventPropertyValue(nbEvent, "MSGSTAMP") != null)
                    stamp = this.getEventPropertyValue(nbEvent, "MSGSTAMP") +
//                            "\t" + t.getTimestamp();
                "\t" + + System.currentTimeMillis();

                this.setEventPropertyValue(nbEvent, "MSGSTAMP", stamp);
                //-----

                if (r2a != null && nbEvent != null) {
                    r2a.addEvent(nbEvent);
                }
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    public long getTimeStamp(){
        try {
            return t.getTimestamp();
        } catch (ServiceException ex) {
            return 0L;
        }
    }

    public static void main(String args[]) {
        if (args.length < 5) {
            System.out.println("Usage: java cgl.sensorgrid.sopac.gps.filters.ryo2ascii commType nbHost nbPort subTopic pubTopic");
            System.exit(0);
        }
        String commType = args[0];
        String nbHost = args[1];
        String nbPort = args[2];
        String subTopic = args[3];
        String pubTopic = args[4];
        String[] argsa = {commType,
                         nbHost,
                         nbPort,
                         subTopic,
                         pubTopic};
//
//        String[] argsa = {"tcp",
//                         "gf3.ucs.indiana.edu",
//                         "5544",
//                         "/SOPAC/GPS/CRTN_01/RYO1",
//                         "/SOPAC/GPS/CRTN_01/ASCII1"};

        ryoTOascii test = new ryoTOascii(argsa, null);
    }
}
